#include "qviewimage.h"

#include <QMessageBox>
#include <QPainter>
#include <QDebug>
#include <QJsonObject>
#include <QJsonArray>


//#define myDebug(QString str)    {qDebug()<<tr("行号: ")<<  __LINE__ << tr("函数名: ") <<  __FUNCTION__<< str}


QViewImage::QViewImage(QWidget *parent)
    : QWidget(parent)
{
    this->grabKeyboard();
    mSourceImage = new QImage();
    setMouseTracking(true);

    mScale = 1.0;
    mRotation = 0.0;
    mScaleBasePt = QPointF(0, 0);
}

QViewImage::~QViewImage(void)
{
    if(mSourceImage)
        delete mSourceImage;
}

void QViewImage::setImage(const QString fullPath)
{
    if(!mSourceImage->load(fullPath)){
        QMessageBox::information(this,tr("open image"),tr("open image failed!!!"));
        delete mSourceImage;
        mSourceImage = nullptr;
    }

    //dispaly
    zoomToFullExtent();
    return;
}

void QViewImage::setPainterToolType(PAINTER_TOOL_TYPE type)
{
    mPainterType = type;
    qDebug()<<"my painter type:"<<mPainterType;
    if(mPainterType == PAINTER_TOOL_TYPE::PAN){
        mVirtualWallTmp.clear();
        mVirtualWall.clear();
        mVirtualWallStart = false;
    }
}


void QViewImage::zoomToFullExtent()
{
    if (!mSourceImage->isNull())
    {
        double sx = mSourceImage->width() * 1.05 / this->width();
        double sy = mSourceImage->height() * 1.05 / this->height();
        mScale = 1. / qMax(sx, sy);

        qreal x = this->width() - mSourceImage->width() * mScale;
        qreal y = this->height() - mSourceImage->height() * mScale;
        mScaleBasePt = QPointF(x/2, y/2);


    }
    repaint();
    return;
}


void QViewImage::paintEvent(QPaintEvent * event)
{
    QPainter p(this);
    p.fillRect(this->rect(), Qt::gray);

    //show src map
    if(true){
        p.save();
        QTransform transform = getTransform();
        p.setWorldTransform(transform);
        p.drawImage(QPoint(0, 0), *mSourceImage);
        p.restore();
        // qDebug()<<"mScaleBasePt:["<<mScaleBasePt.x()<<","<<mScaleBasePt.y()<<"]";
        // qDebug()<<"mSourceImage:["<<mSourceImage->width()<<","<<mSourceImage->height()<<"]";
        // qDebug()<<"this:["<<this->width()<<","<<this->height()<<"]";
        // qDebug()<<"mScale:"<< mScale;
    }

    //todo paint virtual
    updateVirtualWalls();
}

void QViewImage::wheelEvent(QWheelEvent *event)
{
    if (!mSourceImage->isNull())
    {
        QPoint angle = event->angleDelta();

        if (event->modifiers() == Qt::ShiftModifier)   //shift+wheel  rotation
        {
//            mRotation += angle.y()/8;
        }
        else if(event->modifiers() == Qt::CTRL)  //wheel  scale
        {
            QPointF pt1 = event->posF();
            dealScaleAction(pt1, angle);


        }
        repaint();
    }
}

void QViewImage::dealScaleAction(const QPointF& pt,const QPoint& angle)
{
    QPointF pt0(0,0);
    QPointF p = worldToScreen(pt0);

    QPointF pt2;
    if (angle.y() > 0)
    {
        pt2 = pt + (p-pt)*0.9;
        mScale *= 0.9;
    }
    else
    {
        pt2 = pt + (p-pt)/0.9;
        mScale /= 0.9;
    }

    QTransform rotationTransform;
    QPointF c(mSourceImage->width() / qreal(2), mSourceImage->height() / qreal(2));
    rotationTransform.translate(c.x(), c.y());
    rotationTransform.rotate(mRotation);
    rotationTransform.translate(-c.x(), -c.y());

    QTransform scalingTransform;
    scalingTransform.scale(mScale, mScale);

    QPointF a3 = (scalingTransform.map(rotationTransform.map(QPointF(0, 0)))); //ok

    QPointF pt3 = pt2 - a3;
    mScaleBasePt = pt3;//pt1 + pt3;//(p-pt1)/0.618;
}

void QViewImage::resizeEvent( QResizeEvent * event )
{
    zoomToFullExtent();
}

void QViewImage::mouseDoubleClickEvent( QMouseEvent * event )
{
    if(event->button() == Qt::LeftButton)
    {




    }
}

void QViewImage::mouseMoveEvent(QMouseEvent * event)
{
//    qDebug()<<"mouseMoveEvent"<<mVirtualWallStart;
    if(mVirtualWallStart){
        mVirtualWallTmp.clear();
        mVirtualWallTmp.push_back(mVirtualWall.first());
        QPointF pt0 = event->pos();
        QPointF pt1 = screenToWorld(pt0, false);
        mVirtualWallTmp.push_back(pt1);
        repaint();
    }
    if (mButtonLeftPressed)
    {
//        qDebug()<<"pos:["<<event->pos().x()<<","<<event->pos().y()<<"]";
    }
    else if (mButtonRightPressed)
    {
        QPointF mv = event->pos() - mLastPressedMousePt;
        mScaleBasePt += mv;
        mLastPressedMousePt = event->pos();
        repaint();
    }

}

void QViewImage::mousePressEvent(QMouseEvent * event)
{
    mLastPressedMousePt = event->pos();
    if(event->button() == Qt::LeftButton){
        mButtonLeftPressed = true;


        addVirtualWalls();
    }
    else if(event->button() == Qt::RightButton){
        mButtonRightPressed = true;
    }

}

void QViewImage::mouseReleaseEvent(QMouseEvent * event)
{
    mButtonLeftPressed = false;
    mButtonRightPressed = false;

}

void QViewImage::keyPressEvent(QKeyEvent * event)
{
//    qDebug("key=%d",event->key());
    if(event->key() == Qt::Key_Escape){
        setPainterToolType(PAINTER_TOOL_TYPE::PAN);

        zoomToFullExtent();

    }

    //ctrl+S
    if(event->key() == Qt::Key_S && event->modifiers() == Qt::ControlModifier){
        qDebug()<<"ctrl+s";
        saveData();

    }
}

QTransform QViewImage::getTransform(bool includeRotation)
{
    QTransform transform;
    if (!mSourceImage->isNull())
    {
        transform.translate(mScaleBasePt.x(), mScaleBasePt.y());

        transform.scale(mScale, mScale);
        if (includeRotation)
        {
            QPointF c(mSourceImage->width() / qreal(2), mSourceImage->height() / qreal(2));
            transform.translate(c.x(), c.y());
            transform.rotate(mRotation);
            transform.translate(-c.x(), -c.y());
        }
    }
    return transform;
}

QPointF QViewImage::screenToWorld( QPointF &pt, bool includeRotation/* = true*/ )
{
    QTransform t = getTransform(includeRotation);
    QTransform t1 = t.inverted();
    return t1.map(pt);
}

QPointF QViewImage::worldToScreen( QPointF &pt, bool includeRotation/* = true*/ )
{
    QTransform t = getTransform(includeRotation);
    return t.map(pt);
}

void QViewImage::updateVirtualWalls()
{
    QVector<QLineF> lines;
    if(!mVirtualWallVec.isEmpty() || mVirtualWallTmp.size()==2){
        int len = mVirtualWallVec.size();
        for(int i =0; i < len; i++){
            lines.append(QLineF(mVirtualWallVec[i][0], mVirtualWallVec[i][1]));
        }
        if(mVirtualWallTmp.size()==2)
            lines.append(QLineF(mVirtualWallTmp[0], mVirtualWallTmp[1]));

//        qDebug()<<lines;
        paintLinesInSrcImage(lines);
    }
}

void QViewImage::paintLinesInSrcImage(const QVector<QLineF> lines)
{
    QPainter painter(this);
    painter.save();
    QTransform transform = getTransform(false);
    painter.setWorldTransform(transform);

    QPen pen(Qt::red);
    QTransform t1 = transform.inverted();
    QPointF pt = t1.map(QPointF(0, 0));
    QPointF pt1 = t1.map(QPointF(1, 0));
    qreal width = abs((int)(pt1.x() - pt.x()));
    pen.setWidthF(width);
    painter.setPen(pen);
    painter.drawLines(lines);
    painter.restore();
}

void QViewImage::paintPointInSrcImage(const QPointF point)
{
    QPainter painter(this);
    painter.save();
    QTransform transform = getTransform(false);
    painter.setWorldTransform(transform);

    QPen pen(Qt::red);
    QTransform t1 = transform.inverted();
    QPointF pt = t1.map(QPointF(0, 0));
    QPointF pt1 = t1.map(QPointF(10, 0));
    qreal width = abs((int)(pt1.x() - pt.x()));
    pen.setWidthF(width);
    painter.setPen(pen);
    painter.drawPoint(point);
    painter.restore();
}

void QViewImage::addVirtualWalls()
{
    if(mPainterType == PAINTER_TOOL_TYPE::ADD_VIRTUAL_WALL){
        if(!mVirtualWallStart){
            mVirtualWallStart = true;
            qDebug()<<"start paint virtual wall.";
            mVirtualWall.clear();
        }

        if(mVirtualWallStart){
            if(mVirtualWall.size() < 2){
                QPointF pt1 = screenToWorld(mLastPressedMousePt, false);
                mVirtualWall.push_back(pt1);
                qDebug("mVirtualWall virtua wall point");
                if(mVirtualWall.size() == 2){
                    // 检测线是否在图像内
                    if(!checkLineInImage(QLineF(mVirtualWall[0],mVirtualWall[1]))){
                        QMessageBox::information(this, "check", "The line isn't invaild!");
                    }else{
                        mVirtualWallVec.push_back(mVirtualWall);
                        emitSendData(mVirtualWallVec, mSourceImage->width(), mSourceImage->height());
                    }

                    mVirtualWallTmp.clear();
                    mVirtualWall.clear();
                    mVirtualWallStart = false;
                }
            }
            repaint();
        }
    }

    return;

}


bool QViewImage::checkLineInImage(const QLineF& line)
{
    if(checkPointInImage(line.p1()) && checkPointInImage(line.p2()))
        return true;

    return false;
}

bool QViewImage::checkPointInImage(const QPointF& pt)
{
    if(pt.x() > 0 && pt.x() < mSourceImage->width() && pt.y() > 0 && pt.y() < mSourceImage->height())
        return true;
    return false;
}

void QViewImage::saveData()
{
    if(!mVirtualWallVec.isEmpty()){
        int len = mVirtualWallVec.size();
        for(int i = 0; i < len; i++){
            qDebug()<<"mVirtualWallVec["<<i<<"]:["<<mVirtualWallVec[i][0].x()<<","<<mVirtualWallVec[i][0].y()<<"]";
            qDebug()<<"mVirtualWallVec["<<i<<"]:["<<mVirtualWallVec[i][1].x()<<","<<mVirtualWallVec[i][1].y()<<"]";
        }
    }

    //emitSaveData
    emitSaveData();

}

void QViewImage::getVirtualWalls(QVector<QVector<QPointF>>& vec)
{
    vec = mVirtualWallVec;
}

void QViewImage::setVirtualWalls(const QJsonObject& obj)
{
    if(obj.contains("VirtualWalls")){
        QJsonValue value = obj.value("VirtualWalls");
        if(value.isArray()){
            QJsonArray array = value.toArray();
            int len = array.size();
            for(int i = 0; i<len;i++){
                QJsonArray arr0 = array.at(i).toArray();
                QPointF pt0;
                QVector<QPointF> vec;
                for(int j = 0; j < arr0.size(); j++){
                    QJsonArray arr1 = arr0.at(j).toArray();

                    pt0 = QPointF(arr1.at(0).toDouble(),arr1.at(1).toDouble());
                    vec.push_back(pt0);

                }


                mVirtualWallVec.push_back(vec);
            }

            int l = mVirtualWallVec.size();
//            for(int i = 0; i < l; i++){
//                qDebug()<<"mVirtualWallVec["<<i<<"]:["<<mVirtualWallVec[i][0].x()<<","<<mVirtualWallVec[i][0].y()<<"]";
//                qDebug()<<"mVirtualWallVec["<<i<<"]:["<<mVirtualWallVec[i][1].x()<<","<<mVirtualWallVec[i][1].y()<<"]";
//            }
            emitSendData(mVirtualWallVec, mSourceImage->width(), mSourceImage->height());
            repaint();
        }
    }
}
